package stream;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @CreateBy zhangdingyi
 * @Date 2022-09-30
 * @Content
 */
public class StreamsLearn {
    String DATA_PATH = "D:\\java学习\\java基础\\java高级特性 --java核心技术卷2\\test.txt";

    public static void main(String[] args) throws IOException {
        StreamsLearn stramsLearn = new StreamsLearn();
        // 流的基本使用
        stramsLearn.countStrLearn();

        //流的创建
        //stramsLearn.createSteams();
        //流的创建 -卷2内容
        // stramsLearn.createSteamsTow();

    }

    /**
     * 直接创建一个 list 集合 里面有具体的数据
     *
     * @return
     */
    public static List<String> getListStr() {
        String contents = "test,fred,test123,fred123,optional,optional123";
        List<String> list = Arrays.asList(contents.split(","));
        return list;
    }

    /**
     * 创建一个对象集合
     *
     * @return
     */
    public static List<BookCase> getListObj() {
        List<BookCase> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            BookCase bookCase = new BookCase();
            bookCase.setId(i);
            bookCase.setUuid(UUID.randomUUID().toString());
            bookCase.setName("aa" + i + UUID.randomUUID().toString());
            bookCase.setPublishTime(System.currentTimeMillis());
            list.add(bookCase);
        }
       /* 这里是测试 toMap 重复数据的解决方案
       BookCase bookCase = new BookCase();
        bookCase.setId(1);
        bookCase.setName("abc");
        list.add(bookCase);*/
        return list;

    }

    /**
     * 创建一个重复的集合
     *
     * @return
     */
    public static List<BookCase> getListObjRepeat() {
        List<BookCase> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            BookCase bookCase = new BookCase();
            bookCase.setId(i);
            bookCase.setUuid("1");
            bookCase.setName("aa");
            bookCase.setPublishTime(System.currentTimeMillis());
            list.add(bookCase);
        }

        return list;

    }

    /**
     * 流的创建
     *
     * @CreateBy zhangdingyi
     */
    void createSteams() throws IOException {
        String content = getDataStr();
        //使用 stream.of 创建 流
        Stream<String> stream = Stream.of(content.split("\\PL+"));
        //这个等价于 -- Stream.of("testa","testb","testc","ddd");
        //这个forEach 一般遍历全部的数据 -因为其 仅是一个视图并非是真正的 for 因此里面如果需要 break 则在这里不会生效
        stream.forEach(dome -> System.out.println("dome -> " + dome));

        //创建一个不包含元素的流
        Stream<Object> empty = Stream.empty();
        System.out.println("empty -> " + empty);
        //因为这里没有值 所以不会执行 以下代码
        empty.forEach(dome -> System.out.println("dome -> " + dome));

        //然后是创建另外一个不包含元素的流，但需要什么值 可以直接进行赋值 -这里又是 给不包含元素的流调用一个值来使用
        //Stream<Object> generate = Stream.generate(() -> "123abc");
        //System.out.println("generate -> " + generate);
        //因为 本来声明的 流是没有值的， 这里Stream.generate(() -> "123abc"); 仅是对 流的值进行一个调用
        //因此下面的数据会 死循环 -因为这里是不断的对值进行调用，因此里面会有无限多个 123abc 字符串
        //generate.forEach(dome -> System.out.println("dome -> " + dome));
        //这里可以生成一个 无限随机数的流 ---
        // 它会接受一个“种子”值，以及一个函数（从技术上讲，是一个UnaryOperation<T>），并且会反复地将该函数应用到之前的结果上
        //Stream<Object> generate = Stream.generate(Math::random);
        //generate.forEach(dome -> System.out.println("dome -> " + dome));
    }

    /**
     * 这里是 核心技术卷中的代码
     * * @throws IOException
     */
    void createSteamsTow() throws IOException {
        String content = getDataStr();
        //使用 stream.of 创建 流
        //输出 testa,testb,testc,ddd
        Stream<String> word = Stream.of(content.split("\\PL+"));
        show("word", word);

        //输出 testa,testb,testc,ddd
        Stream<String> testa = Stream.of("testa", "testb", "testc", "ddd");
        show("testa", testa);

        //创建一个不包含元素的流
        Stream<String> empty = Stream.empty();
        show("empty", empty);

        //输出 123abc 10个 产生一个无限流，它的值是通过反复调用函数s而构建的
        Stream<String> generate = Stream.generate(() -> "123abc");
        show("generate", generate);

        //输出 随机数 * 10
        Stream<Double> random = Stream.generate(Math::random);
        show("random", random);

        //输出 1~9 -这里虽然看上去是直接添加一，但实际是每个都添加了一再进行存储
        //产生一个无限流，它的元素包含种子、在种子上调用f产生的值、在前一个元素上调用f产生的值
        Stream<BigDecimal> integers = Stream.iterate(BigDecimal.ZERO,
                n -> n.add(BigDecimal.ONE));
        show("integers", integers);

        //输出 testa,testb,testc,ddd
        //产生一个流，它的元素是输入中由该模式界定的部分
        Stream<String> wordsA = Pattern.compile("\\PL+")
                .splitAsStream(content);
        show("wordsA", wordsA);

        //输出 testa,testb,testc,ddd
        //产生一个流，它的元素是指定文件中的行，该文件的字符集为UTF-8，或者为指定的字符集
        Path path = Paths.get(DATA_PATH);
        try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) {
            show("lines", lines);
        }

    }

    /**
     * 显示创建的流数据 --设置成 静态 公有调用
     *
     * @param title  对应流的标题
     * @param stream 传入的流
     * @param <T>    泛型
     */
    public static <T> void show(String title, Stream<T> stream) {
        final int size = 10;
        List<T> list = stream.limit(size + 1).collect(Collectors.toList());
        System.out.println(title + " : ");
        for (int i = 0; i < list.size(); i++) {
            if (i > 0) System.out.print(",");
            if (i < size) System.out.println(list.get(i));
            else System.out.println("....");
        }
        System.out.println();
    }


    /**
     * 基本概念的了解 --怎么使用
     * filter 过滤数据 简单 的说就是满足条件的才要
     * -如果 满足 方法的条件，就 把其进行捕获 ，整合成新的流
     *
     * @throws IOException
     * @CreateBy zhangdingyi
     */
    public void countStrLearn() throws IOException {
        String content = getDataStr();
        List<String> world = Arrays.asList(content.split("\\PL+"));

        System.out.println("world -> " + world);
        long count = 0;
        //流操作
        for (String w : world) {
            if (w.length() > 1) count++;
        }
        System.out.println("count 1 -> " + count);

        //流的 具体使用方式 --这两个的使用 是等价于 for 循环的
        count = world.stream().filter(w -> w.length() > 1).count();
        System.out.println("count 2 = " + count);

        count = world.parallelStream().filter(w -> w.length() > 4).count();
        System.out.println("count 3 = " + count);

    }

    /**
     * 这里直接获取到 路径对应的文件 并且 把其内容转换成字符串
     *
     * @return
     * @throws IOException
     */
    private String getDataStr() throws IOException {
        //System.out.println("\\PL+");
        Path path = Paths.get(DATA_PATH);
        // System.out.println("path -> " + path);
        byte[] bytes = Files.readAllBytes(path);
      /*  System.out.println("bytes -> " + bytes);
        for (byte aByte : bytes) {
            System.out.println("aByte -> " + aByte);
        }*/

        //读取文件
        String content = new String(bytes, StandardCharsets.UTF_8);
        // System.out.println("content.split() = " + content.split("\\PL+"));

        return content;
    }

}
